package com.loan.common.utils.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.loan.common.utils.BeanCopyUtils;
import com.loan.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author shiguang on 2022/6/27 8:52
 */
@Slf4j
public class EasyExcelUtils {

    /**
     * 导出excel到指定目录
     *
     * @param localPath
     * @param fileName
     * @param dataList
     * @param clazz
     * @param limie
     * @param <T>
     * @return
     */
    public static <T> List<String> excelFileUpload(String localPath, String fileName, List<?> dataList, Class<?> clazz, Integer limit) {
        List<String> filesList = new ArrayList<>();
        int number = 0;
        try {
            //判断传入的list数据是否为空
            if (CollectionUtils.isEmpty(dataList)) {
                //如果为空则不需要导出直接返回
                return filesList;
            }
            if (null == limit || limit == 0 || limit > 50000) {
                //默认50000每个excel
                limit = 50000;
            }
            Integer size = dataList.size();
            //判断是否有必要分批
            if (size > limit) {
                //获取分批的数量
                int part = size / limit;
                //判断是不是整数
                int surplus = size % limit;
                if (surplus > 0) {
                    part += 1;
                }
                log.info("导出excel共有:{}条，！分为：{}批", size, part);
                for (int i = 1; i <= part; i++) {
                    number++;
                    String filePath = getFilePath(fileName, number, localPath);
                    filesList.add(filePath);
                    //清除数据
                    if (!CollectionUtils.isEmpty(dataList) && dataList.size() > limit) {
                        EasyExcel.write(filePath, clazz).sheet().doWrite(dataList.subList(0, limit));
                        dataList.subList(0, limit).clear();
                    } else {
                        EasyExcel.write(filePath, clazz).sheet().doWrite(dataList);
                    }
                }
            } else {
                //不需要分批
                String filePath = getFilePath(fileName, number, localPath);
                filesList.add(filePath);
                EasyExcel.write(filePath, clazz).sheet().doWrite(dataList);
            }
        } catch (Exception e) {
            log.error("写excel异常：", e);
            if (CollectionUtils.isEmpty(filesList)) {
                for (String fl : filesList) {
                    File file = new File(fl);
                    if (file.isFile()) {
                        file.delete();
                    }
                }
            }
        }
        return filesList;
    }

    /**
     * 获取导出文件地址，同时判断导出路径是否存在 如果不存在则创建
     *
     * @param fileName
     * @param number
     * @param localPath
     * @return
     */
    private static String getFilePath(String fileName, int number, String localPath) {
        if (StringUtils.isBlank(fileName)) {
            fileName = number + "_" + System.currentTimeMillis() + "";
        } else {
            fileName = fileName + "_" + number + "_" + System.currentTimeMillis();
        }
        String filePath = localPath + File.separator + fileName + ".xls";
        //判断目录是否存在,不存在则创建
        File targetFile = new File(localPath);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        return filePath;
    }

    /**
     * 文件压缩
     *
     * @param localPath
     * @param filesList
     * @param fileName
     * @return
     */
    public static File zipFiles(String localPath, List<String> filesList, String fileName) {
        if (StringUtils.isBlank(fileName)) {
            fileName = File.separator + System.currentTimeMillis() + ".zip";
        } else {
            fileName = File.separator + fileName + ".zip";
        }
        File zipFile = new File(localPath + fileName);
        //判断压缩后的文件是否存在，不存在则创建
        if (!zipFile.exists()) {
            try {
                zipFile.createNewFile();
            } catch (IOException e) {
                log.error("创建文件异常", e);
            }
        }
        FileOutputStream fileOutputStream = null;
        ZipOutputStream zipOutputStream = null;
        FileInputStream fileInputStream = null;
        File file = null;
        try {
            //实例化FileOutputStream对象
            fileOutputStream = new FileOutputStream(zipFile);
            //实例化ZipOutputStream 对象
            zipOutputStream = new ZipOutputStream(fileOutputStream);
            //创建zipEntry对象
            ZipEntry zipEntry = null;
            for (int i = 0; i < filesList.size(); i++) {
                file = new File(filesList.get(i));
                try {
                    fileInputStream = new FileInputStream(file);
                    //实例化 ZipEntry对象，源文件数组中的当前文件
                    zipEntry = new ZipEntry(file.getName());
                    zipOutputStream.putNextEntry(zipEntry);
                    //记录每次真正读的字节个数
                    int length;
                    byte[] buffer = new byte[1024];
                    while ((length = fileInputStream.read(buffer)) > 0) {
                        zipOutputStream.write(buffer, 0, length);
                    }
                } catch (IOException e) {
                    log.error("写zip文件异常", e);
                } finally {
                    fileInputStream.close();
                    if (file.isFile()) {
                        file.delete();
                    }
                }
            }
        } catch (IOException e) {
            log.error("写zip文件异常", e);
            zipFile = null;
        } finally {
            try {
                zipOutputStream.closeEntry();
                zipOutputStream.close();
                fileInputStream.close();
            } catch (IOException e) {
                log.error("关闭流异常", e);
                zipFile = null;
            }
        }
        return zipFile;
    }

    /**
     * 同步返回，不推荐使用，如果数据量过大会把数据放到内存里面
     *
     * @param in
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> readByModel(InputStream in, Class<T> clazz) {
        try {
            //自动读取第一个sheet
            List<Object> list = EasyExcel.read(in).head(clazz).sheet().doReadSync();
            return (List<T>) list;
        } catch (Exception e) {
            log.error("同步读取excel异常", e);
            return null;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取easyexcel 的监听器
     *
     * @param consumer
     * @param threshold
     * @param <T>
     * @return
     */
    public static <T> AnalysisEventListener<T> getListener(Consumer<List<T>> consumer, int threshold) {
        return new AnalysisEventListener<T>() {
            private LinkedList<T> linkedList = new LinkedList<>();

            @Override
            public void invoke(T t, AnalysisContext analysisContext) {
                linkedList.add(t);
                if (linkedList.size() == threshold) {
                    consumer.accept(linkedList);
                    linkedList.clear();
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                if (linkedList.size() > 0) {
                    consumer.accept(linkedList);
                }
            }
        };
    }

    /**
     * 不指定阈值，阈值默认为10
     *
     * @param consumer
     * @param <T>
     * @return
     */
    public static <T> AnalysisEventListener<T> getListener(Consumer<List<T>> consumer) {
        return getListener(consumer, 10);
    }

    public static void exportExcelToTarget(HttpServletResponse response, String fileName,
                                           Collection<?> sourceList, Class<?> targetClass) {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            //这里URLEncoder.encode 可以防止中文乱码
            if (StringUtils.isEmpty(fileName)) {
                fileName = fileName = DateUtils.dateTime(new Date());
            }
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            List<Object> targetList = new ArrayList<>(sourceList.size());
            for (Object source : sourceList) {
                Object target = targetClass.newInstance();
                BeanCopyUtils.copy(source, target);
                targetList.add(target);
            }
            EasyExcel.write(response.getOutputStream(), targetClass).sheet().doWrite(targetList);
        } catch (Exception e) {
            log.error("导出异常", e);
        }
    }

    public static ByteArrayOutputStream excelFileByteUpload(ByteArrayOutputStream outputStream, List<?> list,
                                                            Class<?> clazz, int limit) {
        EasyExcel.write(outputStream,clazz).sheet(limit).doWrite(list);
        return outputStream;
    }

}
